home *** CD-ROM | disk | FTP | other *** search
/ Mastering Internet Develo…oft ActiveX Technologies / Mastering Internet Development with ActiveX (1996)(Microsoft).iso / labs / lab06 / formdump / keys.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1996-07-16  |  29.5 KB  |  1,276 lines

  1. #define WIN32_LEAN_AND_MEAN            // the bare essential Win32 API
  2. #include <windows.h>
  3. #include <httpext.h>
  4.  
  5. //
  6. // If your application plans to receive huge amounts of data
  7. // and you don't want megabytes of memory allocated, define
  8. // the USE_TEMPORARY_FILES.  It is recommended to leave the
  9. // define line below commented out unless absolutely necessary.
  10. //
  11.  
  12. //#define USE_TEMPORARY_FILES
  13.  
  14. #include "keys.h"       
  15.  
  16. #ifndef USE_MEMORY
  17. #ifndef USE_TEMPORARY_FILES
  18. #define USE_MEMORY
  19. #endif
  20. #endif
  21.  
  22. //
  23. // If you want to record errors, modify this macro definition to
  24. // call your own logging function.  This sample does not save
  25. // error strings.
  26. //
  27. #define LOG(errorstring)
  28.  
  29. //
  30. // This file contains four externally used functions, and a bunch of 
  31. // helper functions used only in this file.
  32. //
  33. // Intended external interface:
  34. //
  35. //    GetKeyList        Determines if data was sent, and if it was, the
  36. //                    data is extracted by GetPostKeys or GetUrlKeys,
  37. //                    two private functions within this file.  A 
  38. //                    pointer to a linked list is returned (as a 
  39. //                    handle).
  40. //
  41. //    GetKeyInfo        Returns a pointer to the key name,
  42. //                    the length of the data, a flag indicating if
  43. //                    the data has control characters in it, and an
  44. //                    instance number for duplicate key names.
  45. //
  46. //  GetKeyBuffer    Returns a pointer to the buffer holding the key's
  47. //                    data.
  48. //
  49. //    FindKey            Sequentially searches linked list for key name.
  50. //
  51. //    FreeKeyList        Deallocates memory used by the linked list of
  52. //                    keys.  Also deletes content resources.
  53. //
  54. // Functions provided for the SDK sample IS2WCGI:
  55. //
  56. //    GetKeyOffset    Returns the offset to either the content memory
  57. //                    buffer or the content temporary file.
  58. //
  59. //    GetContentFile     Returns a pointer to the temporary file, only
  60. //                    when USE_TEMPORARY_FILES is defined.
  61. //
  62. //    CloseContentFile Closes the content file, normally left open
  63. //                    until FreeKeyList is called, available only
  64. //                    when USE_TEMPORARY_FILES is defined.
  65. //
  66. //    OpenContentFile Reopens the content file for additional use
  67. //                    by GetKeyBuffer, available only when 
  68. //                    USE_TEMPORARY_FILES is defined.
  69. //
  70. //    GetDataBuffer    Returns a pointer to the content, only if the
  71. //                    USE_TEMPORARY_FILES constant is NOT defined.
  72. //
  73. // Helper functions called only in this source file:
  74. //
  75. //    GetPostedByte    Manages buffer fragmentation, an inherent
  76. //                    characteristic of ReadClient.  Blocks of data
  77. //                    sent by a client are retrieved one byte at a
  78. //                    time.  This works almost like _getch(), except
  79. //                    it returns web data, not keyboard data.
  80. //
  81. //    GetQueryByte    Similar to GetPostedByte, this function
  82. //                    extracts data from the query string.
  83. //
  84. //    HexDigitToInt    Returns the decimal value of a hex character.
  85. //
  86. //    GetFirstByte    Sets up POSDATA struct and calls GetNextByte.
  87. //                    Caller specifies function used to retrieve
  88. //                    data, either GetPostedByte or GetQueryByte.
  89. //
  90. //    GetNextByte        Uses GetInboundByte (specified in GetFirstByte)
  91. //                    to retrieve inbound data, and decodes it using
  92. //                    the URL encoding rules.
  93. //
  94. //    BKL_Alloc        Allocates memory used in GetPostKeys.
  95. //
  96. //    BKL_Dealloc        Deallocates memory used in GetPostKeys.
  97. //
  98. //    BKL_Abort        Cleans up all resources for abnormal exits from
  99. //                    GetPostKeys.
  100. //
  101. //    IsKeySeparator    Returns TRUE if character is one of "=\r\n&\0".
  102. //
  103. //    BuildKeyList    Given a data extraction function (i.e.
  104. //                    GetPostedByte or GetQueryByte), this function
  105. //                    converts all keys into a linked list of POSTKEY
  106. //                    structures.
  107. //
  108. //    GetPostKeys        Takes inbound data off the wire by calling
  109. //                    BuildKeyList with GetPostedByte as the extraction
  110. //                    function.
  111. //
  112. //    GetUrlKeys        Extracts data from the query string by calling
  113. //                    BuildKeyList with GetQueryByte as the extraction
  114. //                    function.
  115. //
  116. //    GetPropAddr        Calculates the address of the list's properties,
  117. //                    appended to the first key in a list.
  118. //
  119. // The typedef for the linked list is kept privately in this file,
  120. // and our interface isolates other source files from the 
  121. // implementation details.
  122. //
  123.  
  124. //
  125. // Constants for this source file only
  126. //
  127.  
  128. #define MAX_KEY_NAME_LENGTH 256        // maximum size of an inbound key name
  129. #define CONTENT_BUF_LENGTH 8192        // amount of content buffered before WriteFile call
  130.                                     // (used for temporary files only)
  131.  
  132. #define GNB_NOTHING_LEFT 0            // GetNextByte return values
  133. #define GNB_DECODED_CHAR 1
  134. #define GNB_NORMAL_CHAR 2
  135.  
  136.  
  137. //
  138. // POSDATA struct is used with GetInboundByte to keep
  139. // track of the position within incoming data.
  140. // GETINBOUNDBYTE is a function pointer type.
  141. //
  142.  
  143. typedef struct _tagPOSDATA
  144.     {
  145.     EXTENSION_CONTROL_BLOCK *pECB;
  146.     int nCurrentPos;        // overall position
  147.     int nBufferLength;        // length of buffer
  148.     int nBufferPos;            // position within buffer
  149.     int nAllocLength;        // size of buffer as allocated
  150.     LPBYTE pData;
  151.     int (*GetInboundByte)(struct _tagPOSDATA *p);
  152.     } POSDATA, *PPOSDATA;
  153.  
  154. typedef int(*GETINBOUNDBYTE)(PPOSDATA p);
  155.  
  156.  
  157. #ifdef USE_MEMORY
  158.  
  159. //
  160. // LISTPROP struct is used to maintain a set of
  161. // list-wide properties.  This implementation
  162. // uses the properties list to hold a buffer
  163. // pointer.
  164. //
  165.  
  166. typedef struct _tagLISTPROP
  167.     {
  168.     LPBYTE    lpbyBuf;
  169.     } LISTPROP, *PLISTPROP;
  170.  
  171. #elif defined USE_TEMPORARY_FILES
  172.  
  173. //
  174. // This LISTPROP struct holds temporary
  175. // file information.
  176. //
  177.  
  178. typedef struct _tagLISTPROP
  179.     {
  180.     TCHAR szTempFileName[MAX_PATH];
  181.     HANDLE hFile;
  182.     } LISTPROP, *PLISTPROP;
  183.  
  184. #endif
  185.  
  186.  
  187. // This private helper needs a prototype
  188. PLISTPROP GetPropAddr (HKEYLIST hKey);
  189.  
  190.  
  191. //
  192. // GetPostedByte returns a waiting character that is not
  193. // decoded yet.  We have this function to smooth out the
  194. // inbound data: the server gives us blocks of data, one at
  195. // a time, and there can be any number of blocks.
  196. //
  197. // For the first call, pPosData->nAllocLength must be zero,
  198. // and pECB must be set.
  199. //
  200.  
  201. int GetPostedByte (PPOSDATA pPosData)
  202.     {
  203.     int nBytesToCopy;
  204.  
  205.     // For readability only...
  206.     EXTENSION_CONTROL_BLOCK *pECB;
  207.     pECB = pPosData->pECB;
  208.  
  209.     //
  210.     // Initialize position struct on first call.
  211.     //
  212.  
  213.     if (!pPosData->nAllocLength)
  214.         {
  215.         // Initialize the members
  216.         pPosData->nCurrentPos = 0;
  217.         pPosData->nBufferPos = 0;
  218.         pPosData->nBufferLength = 0;
  219.         pPosData->nAllocLength = 0x10000;    // 65536 bytes
  220.  
  221.         // Allocate the memory
  222.         pPosData->pData = (LPBYTE) HeapAlloc (GetProcessHeap(), 
  223.                                               HEAP_ZERO_MEMORY, 
  224.                                               pPosData->nAllocLength);
  225.         }
  226.  
  227.     //
  228.     // Was memory allocated?  Is it still allocated?
  229.     // If not, return right away.
  230.     //
  231.  
  232.     if (!pPosData->pData)
  233.         {
  234.         LOG ("GetPostedByte: Buffer not allocated.");
  235.         return -1;
  236.         }
  237.  
  238.     //
  239.     // Check for end.  Deallocate and return if we're done.
  240.     //
  241.  
  242.     if ((DWORD) pPosData->nCurrentPos == pECB->cbTotalBytes)
  243.         {
  244.         HeapFree (GetProcessHeap(), 0, (LPVOID) pPosData->pData);
  245.         pPosData->pData = 0;
  246.         return -1;
  247.         }
  248.  
  249.     //
  250.     // Check for buffer not loaded.  Load if necessary.
  251.     //
  252.  
  253.     if (pPosData->nBufferPos == pPosData->nBufferLength)
  254.         {
  255.         //
  256.         // Fill the buffer with new inbound data.
  257.         // Request it via ReadClient if necessary.
  258.         //
  259.  
  260.         if (pECB->cbAvailable < 1)
  261.             {
  262.             // Calculate how much we should go and get
  263.             nBytesToCopy = pECB->cbTotalBytes - pPosData->nCurrentPos;
  264.             if (nBytesToCopy > pPosData->nAllocLength)
  265.                 nBytesToCopy = pPosData->nAllocLength;
  266.  
  267.             // Let's go get the data
  268.             if (!pECB->ReadClient (pECB->ConnID, pPosData->pData, (LPDWORD) &nBytesToCopy))
  269.                 {
  270.                 HeapFree (GetProcessHeap(), 0, (LPVOID) pPosData->pData);
  271.                 pPosData->pData = 0;
  272.  
  273.                 LOG ("GetPostedByte: Error reading data via ReadClient");
  274.                 return -1;
  275.                 }
  276.             }
  277.         else
  278.             {
  279.             // Take at most nAllocLength bytes of data
  280.             if (pECB->cbAvailable > (DWORD) (pPosData->nAllocLength))
  281.                 nBytesToCopy = pPosData->nAllocLength;
  282.             else
  283.                 nBytesToCopy = pECB->cbAvailable;
  284.  
  285.             // Copy the inbound data to our buffer
  286.             memcpy (pPosData->pData, 
  287.                     &pECB->lpbData[pPosData->nCurrentPos], 
  288.                     nBytesToCopy);
  289.  
  290.             // Account for removed data
  291.             pECB->cbAvailable -= nBytesToCopy;
  292.             }
  293.  
  294.         // Our buffer is now full
  295.         pPosData->nBufferLength = nBytesToCopy;
  296.         pPosData->nBufferPos = 0;
  297.  
  298.         // Make sure we have something
  299.         if (!nBytesToCopy)
  300.             {
  301.             HeapFree (GetProcessHeap(), 0, (LPVOID) pPosData->pData);
  302.             pPosData->pData = 0;
  303.             return -1;
  304.             }
  305.         }
  306.  
  307.     //
  308.     // Inc current pos, buffer pos, and return a character
  309.     //
  310.  
  311.     pPosData->nCurrentPos++;
  312.     pPosData->nBufferPos++;
  313.     return ((int) pPosData->pData[pPosData->nBufferPos - 1]);
  314.     }
  315.  
  316.  
  317. //
  318. // GetQueryByte returns a waiting character that is not
  319. // decoded yet.  We have this function to match GetPostedData.
  320. //
  321. // For the first call, pPosData->nAllocLength must be zero,
  322. // and pECB must be set.
  323. //
  324.  
  325. int GetQueryByte (PPOSDATA pPosData)
  326.     {
  327.     // For readability only...
  328.     EXTENSION_CONTROL_BLOCK *pECB;
  329.     pECB = pPosData->pECB;
  330.  
  331.     //
  332.     // Initialize position struct on first call.
  333.     //
  334.  
  335.     if (!pPosData->nAllocLength)
  336.         {
  337.         // Initialize the useful members
  338.         pPosData->nBufferPos = 0;
  339.         pPosData->nBufferLength = lstrlen ((LPCTSTR) pECB->lpszQueryString);
  340.         pPosData->nAllocLength = -1;
  341.         }
  342.  
  343.     //
  344.     // Check for end.  Deallocate and return if we're done.
  345.     //
  346.  
  347.     if (pPosData->nBufferPos == pPosData->nBufferLength)
  348.         return -1;
  349.  
  350.     //
  351.     // Inc buffer pos and return a character
  352.     //
  353.  
  354.     pPosData->nBufferPos++;
  355.     return ((int) pECB->lpszQueryString[pPosData->nBufferPos - 1]);
  356.     }
  357.  
  358.  
  359. //
  360. // Now that we have GetPostedByte, and GetQueryByte, we can 
  361. // build a more useful function that decodes URL-style 
  362. // encoded characters.
  363. //
  364. // Recall that there are two special cases for this encoding:
  365. //
  366. //  1. Each plus sign must be converted to a space
  367. //  2. A percent sign denotes a hex value-encoded character
  368. //
  369. // Percents are used to specify characters that are otherwise
  370. // illegal.  This includes percents themselves, ampersands,
  371. // control characters, and so on.
  372. //
  373. // GetNextByte returns the decoded byte, plus a flag indicating
  374. // normal character, decoded character, or failure.  See top of  
  375. // file for return value constants.
  376. //
  377.  
  378. //
  379. // HexDigitToInt simply converts a hex-based character to an int.
  380. //
  381.  
  382. int HexDigitToInt (TCHAR tc)
  383.     {
  384.     if (tc >= TEXT('0') && tc <= TEXT('9'))
  385.         return (tc - TEXT('0'));
  386.  
  387.     if (tolower (tc) >= TEXT('a') && tolower (tc) <= TEXT('f'))
  388.         return (tolower (tc) - TEXT('a') + 10);
  389.  
  390.     return -1;
  391.     }
  392.  
  393. //
  394. // GetFirstByte eliminates the guesswork from initialization.
  395. // We call GetFirstByte with an uninitialized POSDATA structure,
  396. // and we call GetNextByte from there on.
  397. //
  398.  
  399. // forward declaration
  400. int GetNextByte (PPOSDATA pPosData, TCHAR *ptc);
  401.  
  402. int GetFirstByte (PPOSDATA pPosData, 
  403.                   EXTENSION_CONTROL_BLOCK *pECB, 
  404.                   TCHAR *ptc, GETINBOUNDBYTE GetInboundByte)
  405.     {
  406.     // Initialize struct
  407.     pPosData->nAllocLength = 0;
  408.     pPosData->pECB = pECB;
  409.     pPosData->GetInboundByte = GetInboundByte;
  410.  
  411.     // Make the call as usual
  412.     return GetNextByte (pPosData, ptc);
  413.     }
  414.  
  415. int GetNextByte (PPOSDATA pPosData, TCHAR *ptc)
  416.     {
  417.     int nChar;
  418.     int nDigit;
  419.  
  420.     // Initialize character pointer
  421.     *ptc = 0;
  422.  
  423.     // Fetch the next inbound character
  424.     nChar = pPosData->GetInboundByte (pPosData);
  425.     if (nChar == -1)
  426.         return GNB_NOTHING_LEFT;
  427.  
  428.     // Plus signs: convert to spaces
  429.     if (nChar == '+')
  430.         {
  431.         *ptc = TEXT(' ');
  432.         return GNB_DECODED_CHAR;
  433.         }
  434.  
  435.     // Percent signs: convert hex values
  436.     else if (nChar == '%')
  437.         {
  438.         nChar = pPosData->GetInboundByte (pPosData);
  439.         nDigit = HexDigitToInt (nChar);
  440.         if (nDigit == -1)
  441.             return GNB_NOTHING_LEFT;
  442.  
  443.         *ptc = (TCHAR) ((UINT) nDigit << 4);
  444.  
  445.         nChar = pPosData->GetInboundByte (pPosData);
  446.         nDigit = HexDigitToInt (nChar);
  447.         if (nDigit == -1)
  448.             {
  449.             *ptc = 0;        // incomplete
  450.             return GNB_NOTHING_LEFT;
  451.             }
  452.  
  453.         *ptc |= (TCHAR) (UINT) nDigit;
  454.  
  455.         return GNB_DECODED_CHAR;
  456.         }
  457.  
  458.     // Must be normal character then
  459.     *ptc = (TCHAR) nChar;
  460.  
  461.     return GNB_NORMAL_CHAR;
  462.     }
  463.  
  464.  
  465. //
  466. // Structure used in data processing - the elements of the
  467. // key list.
  468. //
  469.  
  470. typedef struct _tagPOSTKEY
  471.     {
  472.     int nInstance;        // used when key name is the same as another, normally 0
  473.     DWORD dwOffset;        // offset into content file
  474.     DWORD dwLength;        // length of data
  475.     BOOL bHasCtrlChars;    // a character value < 32 is in data
  476.     struct _tagPOSTKEY *pNext;    // linked list
  477.     struct _tagPOSTKEY *pHead;    // first in linked list
  478.     LPBYTE lpbyBuf;        // pointer to the key's data in the list buffer
  479.  
  480.     // key string appended to structure
  481.     // for the head key, list properties are appended
  482.     } POSTKEY, *PPOSTKEY;
  483.  
  484.  
  485.  
  486. //
  487. // These three helper functions isolates the memory allocation, 
  488. // deallocation and abnormal exit code.  They are used only to 
  489. // keep BuildKeyList readable.
  490. //
  491.  
  492. BOOL BKL_Alloc (LPTSTR *plpszKey, LPBYTE *plpbyBuf)
  493.     {
  494.     // Allocate a buffer for the key name
  495.     *plpszKey = (LPTSTR) HeapAlloc (GetProcessHeap(), 
  496.                                     HEAP_ZERO_MEMORY, 
  497.                                     MAX_KEY_NAME_LENGTH);
  498.     
  499.     if (!*plpszKey)
  500.         return FALSE;
  501.  
  502. #ifdef USE_MEMORY
  503.     // Init buffer to NULL
  504.     *plpbyBuf = NULL;
  505.  
  506. #elif defined USE_TEMPORARY_FILES
  507.  
  508.     // Allocate a buffer for the content
  509.     *plpbyBuf = (LPBYTE) HeapAlloc (GetProcessHeap(), 
  510.                                     HEAP_ZERO_MEMORY, 
  511.                                     MAX_KEY_NAME_LENGTH);
  512.  
  513.     if (!*plpbyBuf)
  514.         {
  515.         HeapFree (GetProcessHeap(), 0, (LPVOID) *plpszKey);
  516.         return FALSE;
  517.         }
  518. #endif
  519.     
  520.     return TRUE;
  521.     }
  522.  
  523. void BKL_Dealloc (LPTSTR *plpsz, LPBYTE *plpby)
  524.     {
  525.     if (*plpsz)
  526.         HeapFree (GetProcessHeap(), 0, (LPVOID) *plpsz);
  527.     if (*plpby)
  528.         HeapFree (GetProcessHeap(), 0, (LPVOID) *plpby);
  529.     }
  530.  
  531. //
  532. // This allows us to clean up... with temporary files we have to close
  533. // and delete them.  Otherwise, we have to free a lot of memory.
  534. //
  535.  
  536. #ifdef USE_TEMPORARY_FILES
  537.  
  538. #define MACRO_AbortCleanup BKL_Abort (pHead,hDataFile,lpszKeyNameBuf,lpbyContentBuf)
  539.  
  540. #elif defined USE_MEMORY
  541.  
  542. #define MACRO_AbortCleanup BKL_Abort (pHead,INVALID_HANDLE_VALUE,lpszKeyNameBuf,lpbyContentBuf)
  543.  
  544. #endif
  545.  
  546. void BKL_Abort (PPOSTKEY pHead, HANDLE hFile, LPTSTR lpszKey, LPBYTE lpbyBuf)
  547.     {
  548.     if (pHead)
  549.         FreeKeyList ((HKEYLIST) pHead);
  550.  
  551.     if (hFile != INVALID_HANDLE_VALUE)
  552.         CloseHandle (hFile);
  553.  
  554.     BKL_Dealloc (&lpszKey, &lpbyBuf);
  555.     }
  556.  
  557. //
  558. // Function used to identify key separators
  559. //
  560.  
  561. BOOL IsKeySeparator (TCHAR tc)
  562.     {
  563.     return (tc == '=' || tc == '\r' || tc == '\n' || tc == '&' || !tc);
  564.     }
  565.  
  566.  
  567. //
  568. // Now that we have a way to get a decoded byte from the stream,
  569. // we can parse POST data.  POST data comes in as:
  570. //
  571. //  key=data&key=data&key=data\r\n
  572. //
  573. // A linked list of keys is established, and the head node
  574. // of the list is returned.  A NULL indicates no keys or
  575. // an error.
  576. //
  577.  
  578. PPOSTKEY BuildKeyList (EXTENSION_CONTROL_BLOCK *pECB, 
  579.                        GETINBOUNDBYTE GetInboundByte)
  580.     {
  581.     PPOSTKEY pHead = NULL;        // head of linked list (the return val)
  582.     PPOSTKEY pTail = NULL;        // last member in linked list
  583.     PPOSTKEY pNewPostKey;        // pointer for unlinked, newly allocated objects
  584.     PPOSTKEY pListWalk;            // linked list walking pointer
  585.  
  586.     PLISTPROP pProp;            // pointer to list properties
  587.  
  588.     LPTSTR lpszKeyNameBuf;        // pointer to buffer, used in obtaining key name
  589.     int nPos;                    // position within key name buffer
  590.  
  591.     DWORD dwOffset;                // offset from start of content buffer or file
  592.     DWORD dwLength;                // length of key data
  593.  
  594.     TCHAR tc;                    // general-purpose character
  595.     int nReturn;                // general-purpose return code
  596.  
  597.     POSDATA pd;                    // POSDATA struct needed in GetInboundByte
  598.  
  599.     int nContentPos;            // position within content buffer
  600.     LPBYTE lpbyContentBuf;        // pointer to buffer
  601.  
  602.     BOOL bHasCtrlChars;            // flag to detect ctrl chars
  603.  
  604.     // Call helper to allocate a buffer
  605.     if (!BKL_Alloc (&lpszKeyNameBuf, &lpbyContentBuf))
  606.         {
  607.         LOG ("BuildKeyList: Memory allocation failure");
  608.         return NULL;
  609.         }
  610.  
  611.     nContentPos = dwOffset = 0;
  612.  
  613.  
  614. #ifdef USE_MEMORY
  615.  
  616.     //
  617.     // Allocate enough memory for all the content.
  618.     // The cbTotalBytes gives us the number of bytes that are
  619.     // being sent by the browser.  We can allocate that much
  620.     // but we'll really only use about 75% of it.
  621.     //
  622.  
  623.     lpbyContentBuf = (LPBYTE) HeapAlloc (GetProcessHeap(), 
  624.                                          HEAP_ZERO_MEMORY, 
  625.                                          pECB->cbTotalBytes);
  626.     if (!lpbyContentBuf)
  627.         {
  628.         LOG ("BuildKeyList: Error allocating content memory");
  629.         BKL_Dealloc (&lpszKeyNameBuf, &lpbyContentBuf);
  630.         return NULL;
  631.         }
  632.  
  633. #elif defined USE_TEMPORARY_FILES
  634.  
  635.     //
  636.     // When USE_TEMPORARY_FILES is chosen, we create
  637.     // a temporary file to store all the inbound data.
  638.     // This is done to support huge amounts of inbound
  639.     // data, like file uploads.
  640.     //
  641.  
  642.     TCHAR szTempDir[MAX_PATH];    // directory of temporary files
  643.     TCHAR szTempPath[MAX_PATH];    // path of content file
  644.     HANDLE hDataFile;            // handle to content file
  645.     DWORD dwBytesWritten;        // used with WriteFile
  646.  
  647.     // Get a temp file name
  648.     GetTempPath (MAX_PATH, szTempDir);
  649.     if (!GetTempFileName (szTempDir, 
  650.                          TEXT("key"), 0, 
  651.                          szTempPath))
  652.         {
  653.         LOG ("BuildKeyList: Error creating temporary file");
  654.         BKL_Dealloc (&lpszKeyNameBuf, &lpbyContentBuf);
  655.         return NULL;
  656.         }
  657.  
  658.     // Create the content file
  659.     hDataFile = CreateFile (szTempPath,
  660.                   GENERIC_READ | GENERIC_WRITE,
  661.                   0,                           // No sharing mode
  662.                   NULL,                        // Default security attribs
  663.                   CREATE_ALWAYS,
  664.                   FILE_ATTRIBUTE_NORMAL,
  665.                   NULL                         // No template file
  666.                   );
  667.  
  668.     // Return if an error occured
  669.     if (hDataFile == INVALID_HANDLE_VALUE)
  670.         {
  671.         LOG ("BuildKeyList: Error opening temporary file");
  672.         MACRO_AbortCleanup;
  673.         return NULL;
  674.         }
  675.  
  676. #endif
  677.  
  678.  
  679.     //
  680.     // 'for' statement detects the start of a valid key name.
  681.     //
  682.     // To do inside 'for' loop:
  683.     //   Obtain key name
  684.     //   Write data to buffer or content file
  685.     //   Create POSTKEY object
  686.     //     Update links
  687.     //
  688.  
  689.     for (nReturn = GetFirstByte (&pd, pECB, &tc, GetInboundByte);
  690.          nReturn != GNB_NOTHING_LEFT;
  691.          nReturn = GetNextByte (&pd, &tc))
  692.         {
  693.         // If \r or \n, ignore and continue
  694.         if (tc == TEXT('\r') || tc == TEXT('\n'))
  695.             continue;
  696.  
  697.         // Get a key name
  698.         nPos = 0;
  699.         while (!IsKeySeparator (tc))
  700.             {
  701.             if (nPos < MAX_KEY_NAME_LENGTH)
  702.                 {
  703.                 lpszKeyNameBuf[nPos] = tc;
  704.                 nPos++;
  705.                 }
  706.  
  707.             nReturn = GetNextByte (&pd, &tc);
  708.             if (nReturn == GNB_NOTHING_LEFT)        // abrupt end!
  709.                 break;
  710.             }
  711.  
  712.         // If no equals sign or name too long,
  713.         // we have a browser formatting error
  714.         if (tc != '=' || nPos == MAX_KEY_NAME_LENGTH)
  715.             {
  716.             LOG ("BuildKeyList: Browser formatting error");
  717.  
  718.             MACRO_AbortCleanup;
  719.             return NULL;
  720.             }
  721.  
  722.         // Truncate the name string, reset data info variables
  723.         lpszKeyNameBuf[nPos] = 0;
  724.         nPos++;
  725.         dwLength = 0;
  726.         bHasCtrlChars = FALSE;
  727.  
  728.         //
  729.         // Move the data to the content buffer or file.
  730.         //
  731.         for (nReturn = GetNextByte (&pd, &tc);
  732.              !IsKeySeparator (tc) || nReturn == GNB_DECODED_CHAR;
  733.              nReturn = GetNextByte (&pd, &tc))
  734.             {
  735.             // Copy inbound data to a content buffer,
  736.             // include support for UNICODE
  737.  
  738.         #ifdef UNICODE
  739.             *((WORD *) (&lpbyContentBuf[nContentPos])) = tc;
  740.         #else
  741.             lpbyContentBuf[nContentPos] = tc;
  742.         #endif
  743.  
  744.             nContentPos += sizeof (TCHAR);
  745.             dwLength++;
  746.  
  747.             // Check for ctrl chars
  748.             if (tc < 0x20 || tc > 0x7e)
  749.                 bHasCtrlChars = TRUE;
  750.  
  751. #ifdef USE_TEMPORARY_FILES
  752.             // If we have enough data, write buffer to disk
  753.             if (nContentPos == CONTENT_BUF_LENGTH)
  754.                 {
  755.                 if (!WriteFile (hDataFile, lpbyContentBuf, 
  756.                             nContentPos, &dwBytesWritten, NULL))
  757.                     {
  758.                     LOG ("BuildKeyList: Error writing to content file");
  759.                     MACRO_AbortCleanup;
  760.                     return NULL;
  761.                     }
  762.  
  763.                 nContentPos = 0;
  764.                 }
  765. #endif
  766.  
  767.             }
  768.  
  769.  
  770. #ifdef USE_MEMORY
  771.  
  772.         // 
  773.         // Put a terminating NULL at the end of the key data.
  774.         //
  775.  
  776.         lpbyContentBuf[nContentPos] = 0;
  777.         nContentPos++;
  778.  
  779. #elif defined USE_TEMPORARY_FILES
  780.  
  781.         // Drain buffer
  782.         if (nContentPos)
  783.             {
  784.             if (!WriteFile (hDataFile, lpbyContentBuf, 
  785.                             nContentPos, &dwBytesWritten, NULL))
  786.                 {
  787.                 LOG ("BuildKeyList: Error writing to content file");
  788.                 MACRO_AbortCleanup;
  789.                 return NULL;
  790.                 }
  791.  
  792.             nContentPos = 0;
  793.             }
  794.  
  795. #endif
  796.  
  797.  
  798.         // Allocate a POSTKEY object, allocate extra for first key
  799.         if (pHead)
  800.             pNewPostKey = (PPOSTKEY) HeapAlloc (GetProcessHeap(), 
  801.                                                 HEAP_ZERO_MEMORY, 
  802.                                                 sizeof (POSTKEY) + nPos);
  803.         else
  804.             {
  805.             pNewPostKey = (PPOSTKEY) HeapAlloc (GetProcessHeap(), 
  806.                                                 HEAP_ZERO_MEMORY, 
  807.                                                 sizeof (POSTKEY) + nPos +
  808.                                                 sizeof (LISTPROP));
  809.             pProp = (PLISTPROP) ((LPBYTE) pNewPostKey + sizeof (POSTKEY) + nPos);
  810.             }
  811.         
  812.         // Check for valid pointer
  813.         if (!pNewPostKey)
  814.             {
  815.             LOG ("BuildKeyList: POSTKEY memory allocation failure");
  816.             MACRO_AbortCleanup;
  817.             return NULL;
  818.             }
  819.  
  820.         //
  821.         // Set pNewPostKey members
  822.         //
  823.  
  824.         // Set nInstance
  825.         pNewPostKey->nInstance = 0;
  826.         pListWalk = pHead;
  827.         while (pListWalk)
  828.             {
  829.             // Check for duplicate key names
  830.             if (!lstrcmpi ((LPCTSTR) (&pListWalk[1]), lpszKeyNameBuf))
  831.                 pNewPostKey->nInstance++;
  832.             pListWalk = pListWalk->pNext;
  833.             }
  834.  
  835.         // Set dwOffset, dwLength, bHasCtrlChars, lpbyBuf
  836.         pNewPostKey->dwOffset = dwOffset;
  837.         pNewPostKey->dwLength = dwLength;
  838.         pNewPostKey->bHasCtrlChars = bHasCtrlChars;
  839.  
  840. #ifdef USE_MEMORY
  841.  
  842.         pNewPostKey->lpbyBuf = &lpbyContentBuf[dwOffset];
  843.         dwOffset += dwLength + 1;
  844.  
  845. #elif defined USE_TEMPORARY_FILES
  846.  
  847.         pNewPostKey->lpbyBuf = NULL;
  848.         dwOffset += dwLength;
  849.  
  850. #endif
  851.  
  852.  
  853.         // Copy key name
  854.         lstrcpy ((LPTSTR) (&pNewPostKey[1]), lpszKeyNameBuf);
  855.  
  856.         // Link
  857.         if (pTail)
  858.             pTail->pNext = pNewPostKey;
  859.         else
  860.             {
  861.  
  862. #ifdef USE_TEMPORARY_FILES
  863.  
  864.             // Copy content file name to list properties
  865.             lstrcpy (pProp->szTempFileName, szTempPath);
  866.  
  867.             // Set handle
  868.             pProp->hFile = hDataFile;
  869.  
  870. #endif
  871.  
  872.             // Set head
  873.             pHead = pNewPostKey;
  874.             }
  875.  
  876.         pNewPostKey->pNext = NULL;
  877.         pTail = pNewPostKey;
  878.  
  879.         pNewPostKey->pHead = pHead;        // may point to itself
  880.         }
  881.  
  882. #ifdef USE_TEMPORARY_FILES
  883.  
  884.     //
  885.     // If content file is empty, close it and delete it
  886.     //
  887.  
  888.     if (!pHead)
  889.         {
  890.         LOG ("Content file is being deleted.");
  891.         CloseHandle (hDataFile);
  892.         DeleteFile (szTempPath);
  893.         }
  894. #endif
  895.  
  896.     return pHead;
  897.     }
  898.  
  899.  
  900. //
  901. // We are now pretty much done with anything complex. BuildKeyList 
  902. // will do all our parse work, so now we need a few wrappers to
  903. // make a nice, clean external interface.
  904. //
  905. // GetPostKeys calls BuildKeyList with GetPostedByte.
  906. //
  907. // GetUrlKeys calls BuildKeyList with GetQueryByte.
  908. //
  909.  
  910. PPOSTKEY GetPostKeys (EXTENSION_CONTROL_BLOCK *pECB)
  911.     {
  912.     return BuildKeyList (pECB, GetPostedByte);
  913.     }
  914.  
  915. PPOSTKEY GetUrlKeys (EXTENSION_CONTROL_BLOCK *pECB)
  916.     {
  917.     return BuildKeyList (pECB, GetQueryByte);
  918.     }
  919.  
  920.  
  921. //
  922. // GetPropAddr returns the address of the end of
  923. // the first key.  We stuff list properties there.
  924. // This implementation of keys.cpp keeps a pointer
  925. // to the content buffer.  The second version (used
  926. // in IS2WCGI) appends a temporary file name
  927. // to the first key.
  928. //
  929.  
  930. PLISTPROP GetPropAddr (HKEYLIST hKey)
  931.     {
  932.     LPCTSTR lpszKeyName;
  933.     PPOSTKEY pHead;
  934.  
  935.     // Safety
  936.     if (!hKey)
  937.         return NULL;
  938.  
  939.     // ContentPath follows POSTKEY struct and key name
  940.     pHead = (PPOSTKEY) hKey;
  941.     pHead = pHead->pHead;
  942.  
  943.     lpszKeyName = (LPCTSTR) (&pHead[1]);
  944.  
  945.     return (PLISTPROP) (lpszKeyName + lstrlen (lpszKeyName) + 1);
  946.     }
  947.  
  948.  
  949. //
  950. // And now we implement the external interface.  GetKeyList
  951. // examines the method and calls GetPostKeys or GetUrlKeys,
  952. // which ever is important.
  953. //
  954.  
  955. HKEYLIST GetKeyList (EXTENSION_CONTROL_BLOCK *pECB)
  956.     {
  957.     if (!lstrcmpi ((LPCTSTR) pECB->lpszMethod, TEXT("POST")))
  958.         {
  959.         LOG ("Method=POST");
  960.         return (HKEYLIST) GetPostKeys (pECB);
  961.         }
  962.  
  963.     else if (!lstrcmpi ((LPCTSTR) pECB->lpszMethod, TEXT("GET")))
  964.         {
  965.         LOG ("Method=GET");
  966.         return (HKEYLIST) GetUrlKeys (pECB);
  967.         }
  968.  
  969.     LOG ("Unknown method");
  970.     return NULL;
  971.     }
  972.  
  973.  
  974. //
  975. // GetKeyInfo is a wrapper for the POSTKEY linked list.
  976. // It returns the members of the supplied POSTKEY object.
  977. //
  978.  
  979. HKEYLIST GetKeyInfo (HKEYLIST hKey, LPCTSTR *plpszKeyName, 
  980.                      LPDWORD pdwLength, BOOL *pbHasCtrlChars, 
  981.                      LPINT pnInstance)
  982.     {
  983.     PPOSTKEY pPostKey;
  984.  
  985.     // Safety
  986.     if (!hKey)
  987.         return NULL;
  988.  
  989.     pPostKey = (PPOSTKEY) hKey;
  990.  
  991.     // Set the data members
  992.     if (plpszKeyName)
  993.         *plpszKeyName = (LPCTSTR) (&pPostKey[1]);
  994.     if (pdwLength)
  995.         *pdwLength = pPostKey->dwLength;
  996.     if (pbHasCtrlChars)
  997.         *pbHasCtrlChars = pPostKey->bHasCtrlChars;
  998.     if (pnInstance)
  999.         *pnInstance = pPostKey->nInstance;
  1000.  
  1001.     // Return a handle to the next object in the list
  1002.     return ((HKEYLIST) pPostKey->pNext);
  1003.     }
  1004.  
  1005.  
  1006. //
  1007. // GetKeyBuffer returns a pointer to the key data.
  1008. // Our data has a terminating NULL too.
  1009. //
  1010.  
  1011. #ifdef USE_MEMORY
  1012.  
  1013. LPBYTE GetKeyBuffer (HKEYLIST hKey)
  1014.     {
  1015.     //
  1016.     // We have two versions of this function because
  1017.     // we may want to use file i/o when the extension
  1018.     // deals with massive amounts of inbound data
  1019.     // (like multi-megabyte uploads).
  1020.     //
  1021.  
  1022.     //
  1023.     // This version uses a memory buffer.
  1024.     //
  1025.  
  1026.     PPOSTKEY pKey;
  1027.  
  1028.     // Safety
  1029.     if (!hKey)
  1030.         return NULL;
  1031.  
  1032.     pKey = (PPOSTKEY) hKey;
  1033.  
  1034.     return (LPBYTE) pKey->lpbyBuf;
  1035.     }
  1036.  
  1037. #elif defined USE_TEMPORARY_FILES
  1038.  
  1039. LPBYTE GetKeyBuffer (HKEYLIST hKey)
  1040.     {
  1041.     //
  1042.     // This version uses slow temporary files.
  1043.     //
  1044.  
  1045.     PLISTPROP pProp;
  1046.     PPOSTKEY pKey;
  1047.     DWORD dwRead;
  1048.  
  1049.     // Get pointer to list properties
  1050.     pProp = GetPropAddr (hKey);
  1051.  
  1052.     // Safety
  1053.     if (!pProp)
  1054.         return NULL;
  1055.  
  1056.     pKey = (PPOSTKEY) hKey;
  1057.  
  1058.     // Check if memory was already loaded for this key
  1059.     if (pKey->lpbyBuf)
  1060.         return pKey->lpbyBuf;
  1061.  
  1062.     // If not, let's allocate memory and do a ReadFile
  1063.     pKey->lpbyBuf = (LPBYTE) HeapAlloc (GetProcessHeap(), 
  1064.                                         HEAP_ZERO_MEMORY, 
  1065.                                         pKey->dwLength + 1);
  1066.     if (!pKey->lpbyBuf)
  1067.         {
  1068.         LOG ("GetKeyBuffer: HeapAlloc failed");
  1069.         return NULL;
  1070.         }
  1071.  
  1072.     // Do the ReadFile
  1073.     SetFilePointer (pProp->hFile, pKey->dwOffset, NULL, FILE_BEGIN);
  1074.     if (!ReadFile (pProp->hFile, pKey->lpbyBuf,
  1075.                    pKey->dwLength, &dwRead, NULL) ||
  1076.                    dwRead != pKey->dwLength)
  1077.         {
  1078.         HeapFree (GetProcessHeap(), 0, (LPVOID) pKey->lpbyBuf);
  1079.         pKey->lpbyBuf = NULL;
  1080.  
  1081.         LOG ("GetKeyBuffer: ReadFile failed");
  1082.         return NULL;
  1083.         }
  1084.     
  1085.     return pKey->lpbyBuf;
  1086.     }
  1087.  
  1088. #endif
  1089.  
  1090. //
  1091. // FindKey sequentially searches the linked list for a given key.
  1092. // The return handle points to the element within the linked list.
  1093. // Use it in GetKeyInfo, but not FreeKeyList.
  1094. //
  1095.  
  1096. HKEYLIST FindKey (HKEYLIST hKeyList, LPCTSTR lpszSearchName)
  1097.     {
  1098.     PPOSTKEY pFindKey;
  1099.  
  1100.     pFindKey = (PPOSTKEY) hKeyList;
  1101.     while (pFindKey)
  1102.         {
  1103.         if (!lstrcmpi (lpszSearchName, (LPCTSTR) (&pFindKey[1])))
  1104.             return ((HKEYLIST) pFindKey);
  1105.  
  1106.         pFindKey = pFindKey->pNext;
  1107.         }
  1108.  
  1109.     return NULL;
  1110.     }
  1111.  
  1112.  
  1113. //
  1114. // FreeKeyList deallocates all the objects in the key list.
  1115. // The content file is also deleted.
  1116. //
  1117.  
  1118. void FreeKeyList (HKEYLIST hHeadKey)
  1119.     {
  1120.     PPOSTKEY pObject;
  1121.     PPOSTKEY pDel;
  1122.  
  1123.     // Safety
  1124.     if (!hHeadKey)
  1125.         return;
  1126.  
  1127.  
  1128. #ifdef USE_TEMPORARY_FILES
  1129.  
  1130.     PLISTPROP pProp;
  1131.  
  1132.     // Close the content file
  1133.     CloseContentFile (hHeadKey);
  1134.  
  1135.     // delete the content file
  1136.     pProp = GetPropAddr (hHeadKey);
  1137.     DeleteFile (pProp->szTempFileName);
  1138.  
  1139. #endif
  1140.  
  1141.  
  1142.     // delete all objects in the list
  1143.     pObject = (PPOSTKEY) hHeadKey;
  1144.     pObject = pObject->pHead;
  1145.     while (pObject)
  1146.         {
  1147.  
  1148. #ifdef USE_TEMPORARY_FILES
  1149.  
  1150.         //
  1151.         // Free each buffer when using temporary files
  1152.         //
  1153.  
  1154.         if (pObject->lpbyBuf)
  1155.             HeapFree (GetProcessHeap(), 0, (LPVOID) pObject->lpbyBuf);
  1156.  
  1157. #endif
  1158.  
  1159.         pDel = pObject;
  1160.         pObject = pObject->pNext;
  1161.  
  1162.         HeapFree (GetProcessHeap(), 0, (LPVOID) pDel);
  1163.         }
  1164.     }
  1165.  
  1166.  
  1167. //
  1168. // GetKeyOffset returns the offset of a key into the internal
  1169. // buffer or temporary file.  This is provided for IS2WCGI
  1170. // so it can return an offset within the content file.
  1171. //
  1172.  
  1173. DWORD GetKeyOffset (HKEYLIST hKey)
  1174.     {
  1175.     // Safety
  1176.     if (!hKey)
  1177.         return NULL;
  1178.  
  1179.     return ((PPOSTKEY) hKey)->dwOffset;
  1180.     }
  1181.  
  1182.  
  1183. #ifdef USE_TEMPORARY_FILES
  1184.  
  1185. //
  1186. // GetContentFile returns a pointer to the name of the
  1187. // temporary file.  This is provided for the IS2WCGI
  1188. // sample.
  1189. //
  1190.  
  1191. LPCTSTR GetContentFile (HKEYLIST hKeyList)
  1192.     {
  1193.     PLISTPROP pProp;
  1194.  
  1195.     // safety
  1196.     if (!hKeyList)
  1197.         return NULL;
  1198.  
  1199.     pProp = GetPropAddr (hKeyList);
  1200.  
  1201.     return (LPCTSTR) pProp->szTempFileName;
  1202.     }
  1203.  
  1204. //
  1205. // CloseContentFile forces the content file to be closed.  This
  1206. // allows you to pass the file to something else that may open
  1207. // it.  Call OpenContentFile before calling any other key
  1208. // function.
  1209. //
  1210.  
  1211. void CloseContentFile (HKEYLIST hKey)
  1212.     {
  1213.     PLISTPROP pProp;
  1214.  
  1215.     if (!hKey)
  1216.         return;
  1217.  
  1218.     pProp = GetPropAddr (hKey);
  1219.     if (pProp->hFile != INVALID_HANDLE_VALUE)
  1220.         {
  1221.         CloseHandle (pProp->hFile);
  1222.         pProp->hFile = INVALID_HANDLE_VALUE;        
  1223.         }
  1224.     }
  1225.  
  1226.  
  1227. //
  1228. // OpenContentFile forces the content file to be reopened.
  1229. // GetKeyBuffer will fail if the content file was closed by
  1230. // CloseContentFile, but not reopened.
  1231. //
  1232.  
  1233. void OpenContentFile (HKEYLIST hKey)
  1234.     {
  1235.     PLISTPROP pProp;
  1236.  
  1237.     if (!hKey)
  1238.         return;
  1239.  
  1240.     pProp = GetPropAddr (hKey);
  1241.  
  1242.     if (pProp->hFile != INVALID_HANDLE_VALUE)
  1243.         return;
  1244.  
  1245.     // Create the content file
  1246.     pProp->hFile = CreateFile (pProp->szTempFileName,
  1247.                               GENERIC_READ | GENERIC_WRITE,
  1248.                               0,                           // No sharing mode
  1249.                               NULL,                        // Default security attribs
  1250.                               OPEN_EXISTING,
  1251.                               FILE_ATTRIBUTE_NORMAL,
  1252.                               NULL);                 
  1253.     }
  1254.  
  1255. #elif defined USE_MEMORY
  1256.  
  1257. //
  1258. // GetBufferPointer returns a pointer to the buffer used
  1259. // for content storage.
  1260. //
  1261.  
  1262. LPBYTE GetDataBuffer (HKEYLIST hKeyList)
  1263.     {
  1264.     PLISTPROP pProp;
  1265.  
  1266.     // safety
  1267.     if (!hKeyList)
  1268.         return NULL;
  1269.  
  1270.     pProp = GetPropAddr (hKeyList);
  1271.  
  1272.     return pProp->lpbyBuf;
  1273.     }
  1274.  
  1275. #endif
  1276.